#include <iostream>
using namespace std;
#include <cmath>
#include <cfloat>

#include "world.h"
#include "custom_utils.h"

// real functions
/*
 * Updates every particle.
 */
void World::update() {
    Particle * current = root;
    Particle * tmp;
    while (current != 0) {
        tmp = root;
        while (tmp != 0) {
            if (tmp != current) {
                current -> beAwareOf(tmp -> get_x(), tmp -> get_y(), tmp -> get_charge(), timeScale);
            }
            tmp = tmp -> next;
        }
        current = current -> next;
    }
    current = root;
    while (current != 0) {
        current -> update(timeScale);
        current = current -> next;
    }
}


/*
 * Adds a particle with the given attributes to the linked list.
 */
void World::addParticle(double x_, double y_, double charge_, double vx_, double vy_, bool locked_) {
    Particle * oldRoot;
    oldRoot = root;
    root = new Particle();
    root -> next = oldRoot;
    root -> set_x(x_);
    root -> set_y(y_);
    root -> set_charge(charge_);
    root -> set_vx(vx_);
    root -> set_vy(vy_);
    root -> set_locked(locked_);
}

/*
 * Removes the particle at the given location.
 */
void World::removeParticle(int n) {
    Particle * toDelete;
    if (n == 0) {
        toDelete = root;
        root = root -> next;
        delete toDelete;
        return;
    }
    Particle * before = getParticle(n - 1);
    if (before == 0) {
        return;
    }
    before -> next = before -> next -> next;
    toDelete = before -> next;
    delete toDelete;
}

/*
 * Displays the contents of the linked list to the console (for debuging purposes).
 */
void World::showParticles() {
    int n = numberOfParticles();
    if (n > 0) {
        cout << "Format: (x, y, charge)" << endl;
    }
    for (int c = 0; c < n; c++) {
        Particle * tmpParticle = getParticle(c);
        cout << c << ": " << "(" << tmpParticle -> get_x() << ", " << tmpParticle -> get_y()
            << ", " << tmpParticle -> get_charge() << ")" << endl;
    }
}

/*
 * Returns a particle based on it's nth root in the linked list (starting at 0)
 */
Particle * World::getParticle(int n) {
    Particle * current = root;
    if (current == 0) {
    return 0;
    }
    for (n; n > 0; n--) {
        if (current -> next != 0) {
            current = current -> next;
        } else {
            return 0;
        }
    }
    return current;
}


/*
 * Returns the closest particle to x_ and y_
 */
Particle * World::closestParticle(double x_, double y_, double maxDistance) {
    Particle * current = root;
    Particle * closest = root;
    double min = DBL_MAX;
    double testDistance;
    if (current == 0) {
        return 0;
    }
    while (current != 0) {
        testDistance = distance(x_, y_, current -> get_x(), current -> get_y());

        if (testDistance < min) {
            closest = current;
            min = testDistance;
        }
        current = current -> next;
    }
    if ((distance(x_, y_, current -> get_x(), current -> get_y())
            <= maxDistance) || (maxDistance == 0)) {
        return closest;
    }
    return 0;
}

/*
 * Returns the number of particles in the linked list.
 */
int World::numberOfParticles() {
    if (root == 0) {
        return 0;
    }
    int c = 1;
    Particle * current = root -> next;
    while (current != 0) {
        c++;
        current = current -> next;
    }
    return c;
}

// functions that deal with variables

// Get
double World::getTimeScale() {
    return timeScale;
}

// Set
World::World() {
    root = 0;
    timeScale = .000005; // TODO: find a way to use the actual time since last update
}

void World::setTimeScale(double time) {
    timeScale = time;
}

